/*
 * Copyright (c) 2021 柯华栋
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ipc_util.h"

// 多线程同步锁相关----------------------------------------------------------------------

static pthread_mutex_t mutex;
static pthread_mutexattr_t attr;

// (如果发现死锁问题，第一时间检查这个函数是否被调用了)
void ipc_init(void)
{
	pthread_mutexattr_init(&attr);
	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
	pthread_mutex_init(&mutex, &attr);
}

void ipc_lock(void)
{	
	pthread_mutex_lock(&mutex);
}

void ipc_unlock(void)
{
	pthread_mutex_unlock(&mutex);
}

// -------------------------------------------------------------------------------------

const gchar* module_get_name_by_id(const gchar* id)
{
	if (id == NULL) 
		return NULL;
	
	const gchar* name_after = strchr(id, '.');
	if (name_after == NULL || name_after-id == 0)
		return NULL;
	
	return g_strndup(id, name_after-id);
}

const gchar* module_get_id_by_map(ipc_map_t* map)
{
	return map == NULL ? NULL : ipc_map_get_string(map, IPC_MAP_KEY_ID);
}

const gchar* module_get_name_by_map(ipc_map_t* map)
{	
	return map == NULL ? NULL 
		: module_get_name_by_id(ipc_map_get_string(map, IPC_MAP_KEY_ID));
}


// 用于on_post, on_send, handle_update中快速寻找id关联的处理函数 ---------------------------
typedef struct 
{
	gchar* 		id;
	gpointer	pointer; 
} id_pointer_map_item_t;


struct _id_pointer_map_t 
{
	int 				size;
	int 				capacity;
	id_pointer_map_item_t**	items;
};

#define get_new_capcity_by_size(x) (((x) + 16) * 3 / 2)

static inline id_pointer_map_item_t* id_pointer_map_item_new(const gchar* id, gpointer pointer)
{
	id_pointer_map_item_t* item = (id_pointer_map_item_t*) malloc(sizeof(id_pointer_map_item_t));
	item->id = str_check_dup(id);
	item->pointer = pointer;
	return item;
}

static inline void id_pointer_map_item_free(id_pointer_map_item_t* item)
{
	str_check_free(item->id);
	free(item);
}

id_pointer_map_t* id_pointer_map_new(void)
{
	id_pointer_map_t* map = (id_pointer_map_t*) malloc(sizeof(id_pointer_map_t));
	assert(map);
	map->size = 0;
	map->capacity = get_new_capcity_by_size(0);
	map->items = (id_pointer_map_item_t**) malloc(sizeof(id_pointer_map_item_t*) * map->capacity);
	return map;
}

void id_pointer_map_free(id_pointer_map_t* map)
{
	if (map == NULL)
		return;
	
	ipc_lock();
	
	for (int i = 0; i < map->size; i++) {		
		id_pointer_map_item_free(map->items[i]);
	}
	free(map->items);
	free(map);
	
	ipc_unlock();
}

static int id_pointer_map_get_item_pos(id_pointer_map_t* map, const gchar* id)
{
	int first = 0;
    int last = map->size;

    while (last > first) {
        int next = (last + first) >> 1;        
        int cmp = strcmp(id, map->items[next]->id);
        if (!cmp)
            return -next - 1;
        if (cmp < 0) {
            last = next;
            continue;
        }
        first = next + 1;
    }
    return first;
}

gpointer id_pointer_map_get(id_pointer_map_t* map, const gchar* id)
{
	if (map == NULL || id == NULL || id[0] == 0) 
		return NULL;	
	ipc_lock();	
	int pos = id_pointer_map_get_item_pos(map, id);
	if (pos < 0) {
		gpointer ret = map->items[-pos-1]->pointer;
		ipc_unlock();
		return ret;
	}
	ipc_unlock();	
	return NULL;
}

void id_pointer_map_set(id_pointer_map_t* map, const gchar* id, gpointer pointer)
{
	if (map == NULL || id == NULL || id[0] == 0) 
		return;
	
	ipc_lock();
	int pos = id_pointer_map_get_item_pos(map, id);
	if (pos < 0) {
		map->items[-pos-1]->pointer = pointer;
		ipc_unlock();
		return;
	}
	
	// 空间满，重新分配
	if (map->size == map->capacity) {
		map->capacity = get_new_capcity_by_size(map->size);
		map->items = (id_pointer_map_item_t**) realloc(map->items, sizeof(id_pointer_map_item_t*) * map->capacity);
	}
	
	// 新建条目添加到pos
	id_pointer_map_item_t* item = id_pointer_map_item_new(id, pointer);
	map->size++;
	// 不添加在末端, 留出位置
	if (map->size > pos) {
		memmove(map->items + pos + 1, map->items + pos, sizeof(id_pointer_map_item_t*) * (map->size - pos - 1));
	}
	map->items[pos] = item;
	
	ipc_unlock();
}

void id_pointer_map_remove(id_pointer_map_t* map, const gchar* id)
{
	if (map == NULL || id == NULL || id[0] == 0) 
		return;
	
	ipc_lock();
	int pos = id_pointer_map_get_item_pos(map, id);
	if (pos < 0) {
		pos = -pos - 1;
		id_pointer_map_item_free(map->items[pos]);
		map->items[pos] = NULL;
		map->size--;
		if (pos < map->size) {
			memmove(map->items + pos, map->items + pos + 1, sizeof(id_pointer_map_item_t*) * (map->size - pos));
		}
	}
	ipc_unlock();
}

int	id_pointer_map_size(id_pointer_map_t* map)
{
	assert(map);
	return map->size;
}

const gpointer id_pointer_map_get_pointer_by_pos(id_pointer_map_t* map, int pos)
{
	assert(map);
	assert(pos >= 0 && pos < map->size);
	return map->items[pos]->pointer;
}

void id_pointer_map_set_pointer_by_pos(id_pointer_map_t* map, int pos, gpointer pointer)
{
	assert(map);
	assert(pos >= 0 && pos < map->size);
	map->items[pos]->pointer = pointer;
}


// gcc -g -Wall ipc_util.c ipc_data.c -o ipc_util `pkg-config --libs --cflags glib-2.0 gio-2.0`